#!/usr/bin/python -w
import subprocess
import math

# This code does not need to be very fast; it will have maybe 2-3 ops per frame.
class Matrix4(object):
    def __init__(self, coefficients):
        """Initialize the matrix from the given coefficients"""
        assert len(coefficients) == 4
        for c in coefficients:
            assert len(c) == 4
        self.coeff = tuple(map(tuple,coefficients))

    def __mul__(self, other):
        return Matrix4([ [ sum(self[i][k] * other[k][j] for k in range(4))
                           for j in range(4) ]
                         for i in range(4) ])

    def __getitem__(self, n):
        return self.coeff[n]

    @classmethod
    def _Rotation(cls, r1, r2, rad):
        c = range(4)
        mat = [ [ (1 if i == j else 0) for i in c ] for j in c]
        mat[r1][r1] = math.cos(rad)
        mat[r1][r2] = -math.sin(rad)
        mat[r2][r1] = math.sin(rad)
        mat[r2][r2] = math.cos(rad)
        return cls(mat)

    @classmethod
    def Identity(cls):
        c = range(4)
        mat = [ [ (1 if i == j else 0) for i in c ] for j in c]
        return cls(mat)

    # SO(4) transformations
    @classmethod
    def RotWX(cls, rad):
        return cls._Rotation(0,1,rad)

    @classmethod
    def RotXY(cls, rad):
        return cls._Rotation(1,2,rad)

    @classmethod
    def RotYZ(cls, rad):
        return cls._Rotation(2,3,rad)

    @classmethod
    def RotZW(cls, rad):
        return cls._Rotation(3,1,rad)

    @classmethod
    def RotWY(cls, rad):
        return cls._Rotation(0,2,rad)

    @classmethod
    def RotXZ(cls, rad):
        return cls._Rotation(1,3,rad)

    def as_list(self):
        return [self[i][j]
                for i in range(4)
                for j in range(4)]

class Driver(object):
    """Helper class for generating movies. Carries basic state between
    frames, which can be modiied using the various modifier functions.
    Use write_image() to save the current frame to disk"""

    def __init__(self):
        self.xform = Matrix4.Identity()
        self.frame = 0


    def write_image(self):

        proc = subprocess.Popen(['./mk_image', '-b', '-i', 'starting_points.txt'],
                                stdin=subprocess.PIPE)
        s = " ".join(map(str, [150,200,  70,170,  0,80] + self.xform.as_list() + ["py_%09d.png" % self.frame])) + "\n"
        print s
        proc.stdin.write(s)
        proc.stdin.close()
        self.frame += 1
        print proc.wait()

    def load_identity(self):
        self.xform = Matrix4.Identity()
        return self

    def rot_WX(self,r):
        self.xform *= Matrix4.RotWX(r)
        return self

    def rot_XY(self,r):
        self.xform *= Matrix4.RotXY(r)
        return self

    def rot_YZ(self,r):
        self.xform *= Matrix4.RotYZ(r)
        return self

    def rot_ZW(self,r):
        self.xform *= Matrix4.RotZW(r)
        return self

    def rot_WY(self,r):
        self.xform *= Matrix4.RotWY(r)
        return self

    def rot_XZ(self,r):
        self.xform *= Matrix4.RotXZ(r)
        return self


    # Shortcuts...
    I = load_identity

    WX = rot_WX
    XY = rot_XY
    YZ = rot_YZ
    ZW = rot_ZW
    WY = rot_WY
    XZ = rot_XZ
    F = write_image

def main():
    d = Driver()
    d.load_identity()

    for x in range(4096):
        print x
        d.WY(0.01).XZ(0.01).XY(0.005).write_image()


if __name__ == '__main__':
    main()
